package com.lu.config;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.lu.model.exception.LuBootException;
import com.lu.model.exception.InvalidTokenException;
import com.lu.model.response.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Set;

/**
 * @program lu-boot-saas
 * @description: 全局异常
 * @author: zhanglu
 * @create: 2022-09-26 14:25:00
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(value = BindException.class)
  public Result<Void> violationException(BindException exception) {
    // 不带任何参数访问接口,会抛出 BindException
    // 因此，我们只需捕获这个异常，并返回我们设置的 message 即可
    String message = exception.getAllErrors().get(0).getDefaultMessage();
    return Result.error(400, message);
  }

  @ExceptionHandler({ConstraintViolationException.class})
  public Result<Void> violationException(Exception exception) {
    if (exception instanceof ConstraintViolationException) {
      // 使用关键字instanceof 判断 ConstraintViolationException 是否为 Exception 直接或间接子类
      // 调用下面方法，返回结果
      return constraintViolationException((ConstraintViolationException) exception);
    }
    // 否则跑出 server error
    return Result.error(500, "server error");
  }

  /** 当我们没有此方法， 会抛出ConstraintViolationException 异常 */
  public Result<Void> constraintViolationException(ConstraintViolationException ex) {
    Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
    // 判断是否为空
    if (!CollectionUtils.isEmpty(constraintViolations)) {
      StringBuilder sb = new StringBuilder();
      // 遍历 ConstraintViolation
      for (ConstraintViolation<?> constraintViolation : constraintViolations) {
        // 把错误信息循环放到sb中, 并以逗号隔开
        sb.append(constraintViolation.getMessage()).append(",");
      }
      // 获得异常信息字符串
      String errorMessage = sb.toString();
      return Result.error(400, errorMessage);
    }
    // 否则跑出 server error
    return Result.error(500, "server error");
  }

  @ExceptionHandler(value = IllegalArgumentException.class)
  public Result<String> illegalParameterException(IllegalArgumentException e) {
    String message = e.getMessage();
    log.info("IllegalArgumentException ==> {}", message);
    return Result.error(2000, message);
  }

  @ResponseStatus(HttpStatus.UNAUTHORIZED)
  @ExceptionHandler(value = InvalidTokenException.class)
  public Result<String> invalidTokenException(InvalidTokenException e) {
    String message = e.getMessage();
    log.info("lu-boot InvalidTokenException ==> {}", message);
    return Result.error(-1, message);
  }

  @ResponseStatus(HttpStatus.FORBIDDEN)
  @ExceptionHandler(value = AccessDeniedException.class)
  public Result<String> accessDeniedException(AccessDeniedException e) {
    log.error("lu-boot AccessDeniedException：" + e);
    return Result.error(-1, "当前权限不足!");
  }


  /**
   * 运行时异常
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = LuBootException.class)
  public Result<String> hxflAssertException(LuBootException e) {
    log.error("lu-boot HxflAssertException：" + e);
    return Result.error(-1, e.getMessage());
  }

  /**
   * 运行时异常
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = RuntimeException.class)
  public Result<String> runtimeException(RuntimeException e) {
    log.error("lu-boot RuntimeException：" + e);
    return Result.error(-1, "服务繁忙");
  }

  /**
   * 全局异常兜底处理
   *
   * @param e 异常
   * @return 响应结果
   */
  @ExceptionHandler(value = Exception.class)
  public Result<String> exception(Exception e) {
    log.error("lu-boot Exception：" + e);
    return Result.error(-1, "服务繁忙");
  }

}
